export const globalInjectModules: Record<string, string> = Object.create(null)

export interface VirtualModuleInfo<Exports extends string> {
  id: string
  exports: readonly Exports[]
  code: string
}

type _generateImports = {
  <II1 extends VirtualModuleInfo<string>, IM1 extends II1['exports'][number]>(
    import1: [
      info: II1,
      imports?: readonly IM1[] | { [name in IM1]: string },
      _default?: 'default' extends II1['exports'][number] ? string : undefined
    ]
  ): string
  <
    II1 extends VirtualModuleInfo<string>,
    IM1 extends II1['exports'][number],
    II2 extends VirtualModuleInfo<string>,
    IM2 extends II2['exports'][number]
  >(
    import1: [
      info: II1,
      imports?: readonly IM1[] | { [name in IM1]: string },
      _default?: 'default' extends II1['exports'][number] ? string : undefined
    ],
    import2: [
      info: II2,
      imports?: readonly IM2[] | { [name in IM2]: string },
      _default?: 'default' extends II2['exports'][number] ? string : undefined
    ]
  ): string
  <
    II1 extends VirtualModuleInfo<string>,
    IM1 extends II1['exports'][number],
    II2 extends VirtualModuleInfo<string>,
    IM2 extends II2['exports'][number],
    II3 extends VirtualModuleInfo<string>,
    IM3 extends II3['exports'][number]
  >(
    import1: [
      info: II1,
      imports?: readonly IM1[] | { [name in IM1]: string },
      _default?: 'default' extends II1['exports'][number] ? string : undefined
    ],
    import2: [
      info: II2,
      imports?: readonly IM2[] | { [name in IM2]: string },
      _default?: 'default' extends II2['exports'][number] ? string : undefined
    ],
    import3: [
      info: II3,
      imports?: readonly IM3[] | { [name in IM3]: string },
      _default?: 'default' extends II3['exports'][number] ? string : undefined
    ]
  ): string
  <
    II1 extends VirtualModuleInfo<string>,
    IM1 extends II1['exports'][number],
    II2 extends VirtualModuleInfo<string>,
    IM2 extends II2['exports'][number],
    II3 extends VirtualModuleInfo<string>,
    IM3 extends II3['exports'][number],
    II4 extends VirtualModuleInfo<string>,
    IM4 extends II4['exports'][number],
    II5 extends VirtualModuleInfo<string>,
    IM5 extends II5['exports'][number],
    II6 extends VirtualModuleInfo<string>,
    IM6 extends II6['exports'][number],
    II7 extends VirtualModuleInfo<string>,
    IM7 extends II7['exports'][number],
    II8 extends VirtualModuleInfo<string>,
    IM8 extends II8['exports'][number],
    II9 extends VirtualModuleInfo<string>,
    IM9 extends II9['exports'][number]
  >(
    import1: [
      info: II1,
      imports?: readonly IM1[] | { [name in IM1]: string },
      _default?: 'default' extends II1['exports'][number] ? string : undefined
    ],
    import2: [
      info: II2,
      imports?: readonly IM2[] | { [name in IM2]: string },
      _default?: 'default' extends II2['exports'][number] ? string : undefined
    ],
    import3: [
      info: II3,
      imports?: readonly IM3[] | { [name in IM3]: string },
      _default?: 'default' extends II3['exports'][number] ? string : undefined
    ],
    import4: [
      info: II4,
      imports?: readonly IM4[] | { [name in IM4]: string },
      _default?: 'default' extends II4['exports'][number] ? string : undefined
    ],
    import5?: [
      info: II5,
      imports?: readonly IM5[] | { [name in IM5]: string },
      _default?: 'default' extends II5['exports'][number] ? string : undefined
    ],
    import6?: [
      info: II6,
      imports?: readonly IM6[] | { [name in IM6]: string },
      _default?: 'default' extends II6['exports'][number] ? string : undefined
    ],
    import7?: [
      info: II7,
      imports?: readonly IM7[] | { [name in IM7]: string },
      _default?: 'default' extends II7['exports'][number] ? string : undefined
    ],
    import8?: [
      info: II8,
      imports?: readonly IM8[] | { [name in IM8]: string },
      _default?: 'default' extends II8['exports'][number] ? string : undefined
    ],
    import9?: [
      info: II9,
      imports?: readonly IM9[] | { [name in IM9]: string },
      _default?: 'default' extends II9['exports'][number] ? string : undefined
    ],
    ...imports: [
      info: VirtualModuleInfo<string>,
      imports: readonly string[] | { [name: string]: string },
      _default?: string
    ][]
  ): string
}

export const generageImports: _generateImports = ((
  ...imports: [
    info: VirtualModuleInfo<string>,
    imports: readonly string[] | { [name: string]: string },
    _default?: string
  ][]
) => {
  return `\n${imports
    .filter(Boolean)
    .map(([{ id }, imports, _default]) => {
      let namedSpecStr: string = ''
      if (!Array.isArray(imports)) {
        const specs: string[] = []
        for (const name in imports) {
          const nameAs = (imports as any)[name]
          if (nameAs && typeof nameAs === 'string') {
            specs.push(`${name} as ${nameAs}`)
          }
        }
        imports = specs
      }
      if (Array.isArray(imports) && imports.length > 0) {
        namedSpecStr = '{' + [...new Set(imports.filter((i) => i !== 'default'))].join(',') + '}'
      }

      const specs = [_default, namedSpecStr].filter((s) => s).join(',')
      return specs
        ? `/*#__PURE__*/ import ${specs} from ${JSON.stringify(id)};`
        : `import ${JSON.stringify(id)};`
    })
    .join('\n')}\n`
}) as any

export const defineVirtualModule = <Exports extends string>(
  id: string,
  code: string | (() => string),
  exports: readonly Exports[] = []
): VirtualModuleInfo<Exports> => {
  const exportsStatment =
    exports && exports.length > 0
      ? `export {${[...new Set(exports.filter((e) => e !== 'default'))].join(',')}};`
      : ''

  if (typeof code === 'function') {
    const genCode = code
    const getCode = () => {
      const code = genCode()

      return `\n${code}\n${exportsStatment}`
    }
    Object.defineProperty(globalInjectModules, id, {
      enumerable: true,
      get: getCode,
    })
    return {
      id,
      get code() {
        return getCode()
      },
      exports,
    }
  } else {
    code = `\n${code}\n${exportsStatment}`
    globalInjectModules[id] = code
    return {
      id,
      code,
      exports,
    }
  }
}
